package msu.ml.core;

import java.util.*;
import java.io.*;

/**
 * The BenchMarkEngine provides a means by which methods
 * can be timed. Simply surrounding code of interest with 
 * start and stop calls will print timing information.
 *
 * @author Reginald M Mead
 * @version 1.0
 */
public class BenchMarkEngine
{
   private static Stack<BenchMarkRecord> stack = new Stack<BenchMarkRecord>();

   /**
    * Start a benchmark context with the given name.
    * Output will use System.out
    *
    * @param name the name of the new context
   */
   public static void start(String name)
   {
      BenchMarkEngine.start(name, System.out);
   }

   /**
    * Start a benchmark context with the given name.
    * No output will be generated.
    *
    * @param name the name of the new context
   */
   public static void startSilently(String name)
   {
      BenchMarkEngine.start(name, null);
   }

   /**
    * Start a benchmark context with the given name.
    * Output will use the specified PrintStream.
    *
    * @param name the name of the new context
    * @param out PrintStream to write output to
   */
   public static void start(String name, PrintStream out)
   {
      stack.push(new BenchMarkRecord(name, System.currentTimeMillis()));
      if(out != null)
      {
         out.println("Start " + name + ":");
      }
   }

   /**
    * Stop the current benchmark context without
    * generating output.
    *
    * @return the ellapsed time since this context was started.
   */
   public static double stopSilently()
   {
      return BenchMarkEngine.stop(null);
   }

   /**
    * Stop the current benchmark context and output
    * timing information to System.out
    *
    * @return the ellapsed time since this context was started.
   */
   public static double stop()
   {
      return BenchMarkEngine.stop(System.out);
   }

   /**
    * Stop the current benchmark context and output
    * timing information to the specified PrintStream
    *
    * @param out PrintStream to write output to
    * @return the ellapsed time since this context was started.
   */
   public static double stop(PrintStream out)
   {
      BenchMarkRecord record = stack.pop();
      double ellapsedTime = (double)(System.currentTimeMillis() - record.time) / 1000.0;

      if(out != null)
      {
         out.println(String.format("End %1$s: %2$6.3fs ellapsed", record.name, ellapsedTime));
      }

      return ellapsedTime;
   }

   private static class BenchMarkRecord
   {
      public long time;
      public String name;

      public BenchMarkRecord(String name, long time)
      {
         this.name = name;
         this.time = time;
      }
   }
}
